//
// Created by wxd on 2025/7/24.
//

#pragma once

#include <iostream>
#include <vector>
#include <glm/glm.hpp>
#include <glm/trigonometric.hpp>
#include "../wrapper/log/Logger.h"
#include "../wrapper/monitor/GLDebug.hpp"
#include "../global/ChineseOutput.hpp"
#include "../global/EngineDataProperties.h"
#include "../application/app/Application.h"
#include "../glFramework/shader/Shader.h"
#include "../glFramework/constant/engine_layout.h"
#include "../glFramework/texture/Texture.h"
#include "../glFramework/utils/ResourceLoadUtils.h"
#include "../glFramework/bo/VBO.h"
#include "../glFramework/bo/VAO.h"
#include "../glFramework/constant/engine_uniform.h"
#include <memory>

#include "bo/EBO.h"
#include "../global/output.hpp"

struct CameraParams {
    glm::vec3 eye;
    glm::vec3 center;
    glm::vec3 up;
};

struct PerspectiveParams {
    float ratio = 0.f;
    float fov = glm::radians(45.0f); // fov越小，模型越大，fov越大，模型越小
    float zNear = 0.1f; // 注意：这里的必须传递负值
    float zFar = 100.f;
};

struct MvpMatrix {
    glm::mat4 model = glm::mat4(1.0f);
    glm::mat4 view = glm::mat4(1.0f);
    glm::mat4 proj = glm::mat4(1.0f);
};

struct FrameManager {
    float lastFrame = 0.f;
    float currFrame = 0.f;
    float deltFrame = 0.f;
};

struct MoveParams {
    float angleSpeed = 20.f;
    float accAngle = 0.f;

    void updatePeriod() {
        if (accAngle > 360.0f) accAngle -= 360.0f;
    }
};

inline const char* vertexPath = "../demo/shader/_011/vertex_shader.glsl";
inline const char* fragmentPath = "../demo/shader/_011/fragment_shader.glsl";
inline const char* texPath = "../demo/texture/earth.png";
inline FrameManager frameManager;
inline MoveParams moveParams;
inline MvpMatrix mvpMatrix;

inline CameraParams cameraParams;
inline PerspectiveParams perspectiveParams;

inline float radius = 3.0f;

inline void onResize(int w, int h) {
    glViewport(0, 0, w, h);
}

inline void onKeyBoard(int key, int action, int mods) {
    if (key == GLFW_KEY_ESCAPE) {
        glfwSetWindowShouldClose(EngineApp.getCurrentWindow(), true);
    }
}

inline void updateCamera() {
    float x = radius * glm::sin(glm::radians(moveParams.accAngle));
    float z = radius * glm::cos(glm::radians(moveParams.accAngle));
    cameraParams.eye.x = x;
    cameraParams.eye.z = z;
    mvpMatrix.view = glm::lookAt(cameraParams.eye, cameraParams.center, cameraParams.up);
}

inline void render() {
    ChineseOutput::init();
    EngineApp.initDefaultWindowConfig();
    EngineApp.setResizeCallback(onResize);
    EngineApp.setKeyBoardCallback(onKeyBoard);
    EngineApp.init();

    std::vector<float> vertices;
    vertices.reserve(32); // 顶点坐标 + 颜色 + 纹理坐标
    std::vector<unsigned int> indices;
    indices.reserve(6);

    vertices.push_back(0.5f);
    vertices.push_back(0.5f);
    vertices.push_back(0.0f);
    vertices.push_back(1.0f);
    vertices.push_back(0.0f);
    vertices.push_back(0.0f);
    vertices.push_back(1.0f);
    vertices.push_back(1.0f);
    vertices.push_back(0.5f);
    vertices.push_back(-0.5f);
    vertices.push_back(0.0f);
    vertices.push_back(0.0f);
    vertices.push_back(1.0f);
    vertices.push_back(0.0f);
    vertices.push_back(1.0f);
    vertices.push_back(0.0f);
    vertices.push_back(-0.5f);
    vertices.push_back(-0.5f);
    vertices.push_back(0.0f);
    vertices.push_back(0.0f);
    vertices.push_back(0.0f);
    vertices.push_back(1.0f);
    vertices.push_back(0.0f);
    vertices.push_back(0.0f);
    vertices.push_back(-0.5f);
    vertices.push_back(0.5f);
    vertices.push_back(0.0f);
    vertices.push_back(1.0f);
    vertices.push_back(0.0f);
    vertices.push_back(0.0f);
    vertices.push_back(0.0f);
    vertices.push_back(1.0f);

    indices.push_back(0);
    indices.push_back(1);
    indices.push_back(2);
    indices.push_back(0);
    indices.push_back(2);
    indices.push_back(3);

    Texture texture(texPath);
    texture.setSamplerName(EngineUniform::uTexUnit);

    VAO vao;
    vao.bind();
    VBO vbo(vertices);
    EBO ebo(indices);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, fSize(8), reinterpret_cast<void *>(0));
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, fSize(8), reinterpret_cast<void *>(3 * sizeof(float)));
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, fSize(8), reinterpret_cast<void *>(6 * sizeof(float)));
    glEnableVertexAttribArray(2);
    vao.unbind();

    std::unique_ptr<Shader> shader = std::make_unique<Shader>(vertexPath, fragmentPath);

    cameraParams.eye = glm::vec3(0.f, 0.f, 3.0f);
    cameraParams.center = glm::vec3(0.0f, 0.0f, 0.0f);
    cameraParams.up = glm::vec3(0.0f, 1.0f, 0.0f);
    perspectiveParams.ratio = EngineApp.getWindowAspectRatio();

    mvpMatrix.view = glm::lookAt(cameraParams.eye, cameraParams.center, cameraParams.up);
    mvpMatrix.proj = glm::perspective(perspectiveParams.fov, perspectiveParams.ratio, perspectiveParams.zNear, perspectiveParams.zFar);

    while (EngineApp.update()) {
        /**
         * 上述有一个比较奇怪的问题：设置的相机z = 3.0，模型的z = 0.0，我们设置的透视的zNear = 0.1，zFar = 100.
         * 由于相机看向-z，所以这里的正值会被转化为负值，导致正方形根本不在锥体内，本应该被裁剪掉，但实际被正确渲染出来？
         * 原因：没有开启深度测试导致的
         */
        glClear(GL_COLOR_BUFFER_BIT);

        frameManager.currFrame = static_cast<float>(glfwGetTime());
        if (frameManager.lastFrame > 0.f) {
            frameManager.deltFrame = frameManager.currFrame - frameManager.lastFrame;
        }
        frameManager.lastFrame = frameManager.currFrame;

        moveParams.accAngle += moveParams.angleSpeed * frameManager.deltFrame;
        moveParams.updatePeriod();

        updateCamera();

        shader->bindProgram();
        shader->setUniform("uModel", mvpMatrix.model);
        shader->setUniform("uView", mvpMatrix.view);
        shader->setUniform("uProj", mvpMatrix.proj);
        shader->setUniform(texture.getSamplerName(), texture.getTextureUnit()); // 采样0号单元
        vao.bind();
        GL_CALL(glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, reinterpret_cast<void*>(0)))
        vao.unbind();
        shader->unbindProgram();
    }

    vao.destroy();
    vbo.destroy();
    ebo.destroy();
    shader->deleteProgram();
    EngineApp.destroy();
}
